package com.zl.aop.log;

import com.zl.aop.inter.ZLAopCut;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

/**
 * @author zllwsy
 * @Description
 * @Date 2021/7/28 13:48
 * @Created by zllwsy
 */
@Component
@Aspect
public class LogAspect {
    //切入点表达式
    @Pointcut("execution(* com.zl.aop.controller.*.*(..))")
    public void aopCut(){

    }

    //前置通知，也就是在目标方法执行之前执行
    @Before(value = "aopCut()")
    public void before(JoinPoint joinPoint){
        //获取目标方法的名称
        String name = joinPoint.getSignature().getName();
        System.out.println(name + "方法执行之前");
    }

    //后置通知，也就是方法执行之后，获取目标方法的返回值
    @AfterReturning(value = "aopCut()",returning = "result")
    public void afterReturning(JoinPoint joinPoint,Object result){
        String name = joinPoint.getSignature().getName();
        System.out.println(name + "方法执行之后，" + ", 返回值为: " + result);
    }

    //异常通知，也就是当方法出现异常时，调用该方法
    @AfterThrowing(value = "aopCut()",throwing = "e")
    public void afterThrowing(JoinPoint joinPoint,Exception e){
        String name = joinPoint.getSignature().getName();
        System.out.println(name + "方法执行之后，" + "抛出异常: " + e);
    }

    //环绕通知,也就是可实现任意通知，可调用 proceedingJoinPoint.proceed() 方法使目标方法继续执行
    @Around(value = "aopCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        String name = joinPoint.getSignature().getName();
        //获取方法传入的参数
        Object[] args = joinPoint.getArgs();
        //可在这里对方法进行处理,调用自定义注解
        ZLAopCut declaredAnnotation = getDeclaredAnnotation(joinPoint);
        //然后调用proceed继续执行方法
        Object proceed = joinPoint.proceed();
        System.out.println("around");
        return proceed;
    }

    //自定义注解
    public ZLAopCut getDeclaredAnnotation(ProceedingJoinPoint joinPoint)throws NoSuchMethodException {
        // 获取方法名
        String methodName = joinPoint.getSignature().getName();
        // 反射获取目标类
        Class<?> targetClass = joinPoint.getTarget().getClass();
        // 拿到方法对应的参数类型
        Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getParameterTypes();
        // 根据类、方法、参数类型（重载）获取到方法的具体信息
        Method objMethod = targetClass.getMethod(methodName, parameterTypes);
        // 拿到方法定义的注解信息
        ZLAopCut annotation = objMethod.getDeclaredAnnotation(ZLAopCut.class);
        // 返回
        return annotation;
    }
}
